// ------------------------------------------------------------------------------
// 
// Copyright (c) 2008 Swampware, Inc.
// 
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// 
// ------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;

namespace Bamboo.Parsing.Generators.CSharp
{
		public class TokenizerGenerator
		{

			public static void Generate(string name, string nspace, Bamboo.Parsing.FiniteAutomata.FiniteAutomaton finiteAutomaton, System.IO.TextWriter writer)
			{
				writer.WriteLine("//");
				writer.WriteLine("// AUTOGENERATED " + System.DateTime.Now + "");
				writer.WriteLine("//");
				writer.WriteLine("using System;");
				writer.WriteLine("");
				writer.WriteLine("namespace " + nspace + "");
				writer.WriteLine("{");
				writer.WriteLine("	public class " + name + "Tokenizer");
				writer.WriteLine("	{");
				writer.WriteLine("		private System.Text.StringBuilder _stringBuilder = new System.Text.StringBuilder();");
				writer.WriteLine("");
				writer.WriteLine("		public " + name + "Tokenizer()");
				writer.WriteLine("		{");
				writer.WriteLine("		}");
				writer.WriteLine("");
				writer.WriteLine("		public " + name + "Token Tokenize(" + name + "TextReader reader)");
				writer.WriteLine("		{");
				writer.WriteLine("			int n;");
				writer.WriteLine("			char ch;");
				writer.WriteLine("");
				writer.WriteLine("			_stringBuilder.Length = 0;");
				writer.WriteLine("");
				writer.WriteLine("			//");
				writer.WriteLine("			// Trim whitespace");
				writer.WriteLine("			//");
				writer.WriteLine("			while ((n = reader.Peek()) != -1)");
				writer.WriteLine("			{");
				writer.WriteLine("				ch = (char)n;");
				writer.WriteLine("				switch (ch)");
				writer.WriteLine("				{");
				writer.WriteLine("					case ' ':");
				writer.WriteLine("					case '\\t':");
				if (!HasNewline(finiteAutomaton.Tokens))
				{
					writer.WriteLine("					case '\\r':");
					writer.WriteLine("					case '\\n':");
				}
				writer.WriteLine("						{");
				writer.WriteLine("							reader.Read();");
				writer.WriteLine("							break;");
				writer.WriteLine("						}");
				writer.WriteLine("					default:");
				writer.WriteLine("						{");
				writer.WriteLine("							goto s0;");
				writer.WriteLine("						}");
				writer.WriteLine("				}");
				writer.WriteLine("			}");
				writer.WriteLine("");
				foreach (int state in finiteAutomaton.States)
				{
					writer.WriteLine("		s" + state + ":");
					if (Operators.IsFinal(state, finiteAutomaton.FinalStates) && !Operators.HasTransitions(state, finiteAutomaton.Transitions))
					{
						Bamboo.Parsing.FiniteAutomata.Token token = Operators.GetToken(state, finiteAutomaton.Tokens);
						writer.WriteLine("			return new " + name + "Token(" + name + "TokenType." + token.Name + ", _stringBuilder.ToString());");
					}
					else
					{
						writer.WriteLine("			n = reader.Peek();");
						writer.WriteLine("			if (n == -1)");
						writer.WriteLine("			{");
						writer.WriteLine("				if (_stringBuilder.Length == 0)");
						writer.WriteLine("				{");
						writer.WriteLine("					return new " + name + "Token(" + name + "TokenType._EOF_);");
						writer.WriteLine("				}");
						writer.WriteLine("				else");
						writer.WriteLine("				{");
						if (Operators.IsFinal(state, finiteAutomaton.FinalStates))
						{
							Bamboo.Parsing.FiniteAutomata.Token token = Operators.GetToken(state, finiteAutomaton.Tokens);
							writer.WriteLine("					return new " + name + "Token(" + name + "TokenType." + token.Name + ", _stringBuilder.ToString());");
						}
						else
						{
							writer.WriteLine("					return new " + name + "Token(" + name + "TokenType._ERROR_);");
						}
						writer.WriteLine("				}");
						writer.WriteLine("			}");
						writer.WriteLine("			ch = (char)n;");
						writer.WriteLine("			switch (ch)");
						writer.WriteLine("			{");

						Surf.Set transitions = new Surf.Set();
						foreach (Bamboo.Parsing.FiniteAutomata.Transition transition in finiteAutomaton.Transitions)
						{
							if (transition.FromState == state)
							{
								transitions.Add(new Surf.Tuple(new object[] { transition.ToState, transition.Character }));
							}
						}
						transitions = transitions.Nest();
						foreach (Surf.Tuple transition in transitions)
						{
							foreach (char character in (Surf.Set)transition[1])
							{
								writer.WriteLine("				case '" + Operators.Escape(character) + "':");
							}
							writer.WriteLine("					{");
							writer.WriteLine("						reader.Read();");
							writer.WriteLine("						_stringBuilder.Append(ch);");
							writer.WriteLine("						goto s" + transition[0] + ";");
							writer.WriteLine("					}");
						}

						writer.WriteLine("				default:");
						writer.WriteLine("					{");
						if (Operators.IsFinal(state, finiteAutomaton.FinalStates))
						{
							Bamboo.Parsing.FiniteAutomata.Token token = Operators.GetToken(state, finiteAutomaton.Tokens);
							writer.WriteLine("						return new " + name + "Token(" + name + "TokenType." + token.Name + ", _stringBuilder.ToString());");
						}
						else
						{
							writer.WriteLine("						_stringBuilder.Append(ch);");
							writer.WriteLine("						goto se;");
						}
						writer.WriteLine("					}");
						writer.WriteLine("			}");
					}
					writer.WriteLine("");
				}
				writer.WriteLine("		se:");
				writer.WriteLine("			return new " + name + "Token(" + name + "TokenType._ERROR_, _stringBuilder.ToString());");
				writer.WriteLine("");
				writer.WriteLine("		}");
				writer.WriteLine("");
				writer.WriteLine("	}");
				writer.WriteLine("}");
			}

			private static bool HasNewline(Bamboo.Parsing.FiniteAutomata.Token[] tokens)
			{
				foreach (Bamboo.Parsing.FiniteAutomata.Token token in tokens)
				{
					if (token.Name.Equals("NEWLINE"))
					{
						return true;
					}
				}
				return false;
			}

			private TokenizerGenerator()
			{
			}

		}
}
